#include "CHIP_PWM.hpp"

#ifdef DBG_TAG
#undef DBG_TAG
#endif
#ifdef DBG_LVL
#undef DBG_LVL
#endif

#define DBG_TAG     "CHIP_PWM"
#define DBG_LVL     DBG_LOG

extern "C"
{
#include <rtdbg.h>
}

using namespace std;

chip_pwm_status CHIP_PWM::_calculate_pwm_period(uint32_t pwm_frequency)
{
    double period = (double) (1 *1000 * 1000 * 1000 / pwm_frequency);
    if (period == 0)
    {
        LOG_E("error: pwm dev <%s> cant set frequency <%u>", pwm_dev_name.c_str(), pwm_frequency);
        return chip_pwm_status_fail;
    }
    else
    {
        this->pwm_period = period;
        return chip_pwm_status_ok;
    }

}
void CHIP_PWM::_calculate_pwm_pulse_width(double pwm_ratio)
{
    uint32_t result = 0;
    if (pwm_ratio > 100)
    {
        LOG_W("warn:input pwm ratio is more than 100 , change value is set to 100");
        pwm_ratio = 100;
    }
    switch (pwm_type)
    {
    case chip_pwm_drive_type_HIGH:
        break;
    case chip_pwm_drive_type_LOW:
        pwm_ratio = 100 - pwm_ratio;
        break;
    default:
        break;
    }
    result = (uint32_t) (pwm_ratio * pwm_period / 100);
    this->pwm_pulse_width = result;
}
CHIP_PWM::~CHIP_PWM()
{
    if(pwm_ratio_lock)
        rt_mutex_delete(pwm_ratio_lock);
}

CHIP_PWM::CHIP_PWM(std::string pwm_dev_name,
                   int pwm_channel,
                   chip_pwm_drive_type pwm_type,
                   uint32_t pwm_frequency,
                   double default_ratio)
{
    this->pwm_type = pwm_type;
    this->pwm_dev_name = pwm_dev_name;
    this->pwm_dev = (struct rt_device_pwm *) rt_device_find(pwm_dev_name.c_str());
    if (!this->pwm_dev)
    {
        LOG_E("error: pwm dev <%s> not found", pwm_dev_name.c_str());
        goto end;
    }

    if (_calculate_pwm_period(pwm_frequency) != chip_pwm_status_ok)
    {
        LOG_E("error: pwm dev <%s> channel <%d> pwm_frequency <%u> fail",
              pwm_dev_name.c_str(),
              pwm_channel,
              pwm_frequency);
        goto end;
    }

    _calculate_pwm_pulse_width(default_ratio);

    if (rt_pwm_set(this->pwm_dev, pwm_channel, this->pwm_period, this->pwm_pulse_width) != RT_EOK)
    {
        LOG_E("error: pwm dev <%s> channel <%d> set period <%u> pluse width <%u> fail",
              pwm_dev_name.c_str(),
              pwm_channel,
              this->pwm_period,
              this->pwm_pulse_width);
        goto end;
    }
    this->pwm_frequency = pwm_frequency;
    this->pwm_channel = pwm_channel;

    pwm_ratio_lock = rt_mutex_create((pwm_dev_name+"_lock").c_str(), RT_IPC_FLAG_PRIO);
    if(!pwm_ratio_lock)
    {
        LOG_E("error: pwm dev <%s> channel <%d> create ratio lock fail",
              pwm_dev_name.c_str(),
              pwm_channel);
        goto end;
    }

    rt_mutex_take(pwm_ratio_lock, RT_WAITING_FOREVER);
    this->pwm_ratio = default_ratio;
    rt_mutex_release(pwm_ratio_lock);

    LOG_I("pwm dev <%s> channel <%d> pwm frequency <%u> default ratio <%.1f> init ok",
          pwm_dev_name.c_str(),
          pwm_channel,
          pwm_frequency,
          default_ratio);
    this->init_flag = chip_pwm_status_ok;
    this->debug_flag = 0;
    return;
    end:
    LOG_E("pwm dev <%s> channel <%d> pwm frequency <%u> default ratio <%.1f> init fail",
          pwm_dev_name.c_str(),
          pwm_channel,
          pwm_frequency,
          default_ratio);
    this->debug_flag = 0;
    this->init_flag = chip_pwm_status_fail;
    return;
}

void CHIP_PWM::CHIP_PWM_open_debug()
{
    debug_flag = 1;
}

void CHIP_PWM::CHIP_PWM_close_debug()
{
    debug_flag = 0;
}

chip_pwm_status CHIP_PWM::CHIP_PWM_open()
{
    if(init_flag != chip_pwm_status_ok)
    {
        LOG_E("error: pwm dev <%s> channel <%d> init fail,pls check",
              pwm_dev_name.c_str(),
              pwm_channel);
        return chip_pwm_status_fail;
    }

    if(rt_pwm_enable(this->pwm_dev, pwm_channel) != RT_EOK)
    {
        LOG_E("error: pwm dev <%s> channel <%d> enable channel fail",
              pwm_dev_name.c_str(),
              pwm_channel);
        return chip_pwm_status_fail;
    }
    return chip_pwm_status_ok;
}

chip_pwm_status CHIP_PWM::CHIP_PWM_close()
{
    if(init_flag != chip_pwm_status_ok)
    {
        LOG_E("error: pwm dev <%s> channel <%d> init fail,pls check",
              pwm_dev_name.c_str(),
              pwm_channel);
        return chip_pwm_status_fail;
    }

    if(rt_pwm_disable(this->pwm_dev, pwm_channel) != RT_EOK)
    {
        LOG_E("error: pwm dev <%s> channel <%d> disable channel fail",
              pwm_dev_name.c_str(),
              pwm_channel);
        return chip_pwm_status_fail;
    }
    return chip_pwm_status_ok;
}

chip_pwm_status CHIP_PWM::CHIP_PWM_change_channel(int new_channel)
{
    if(init_flag != chip_pwm_status_ok)
    {
        LOG_E("error: pwm dev <%s> channel <%d> init fail,pls check",
              pwm_dev_name.c_str(),
              pwm_channel);
        return chip_pwm_status_fail;
    }

    rt_pwm_disable(this->pwm_dev, pwm_channel);

    if (rt_pwm_set(this->pwm_dev, new_channel, this->pwm_period, this->pwm_pulse_width) != RT_EOK)
    {
        LOG_E("error: pwm dev <%s> channel <%d> set period <%u> pluse width <%u> fail",
              pwm_dev_name.c_str(),
              pwm_channel,
              this->pwm_period,
              this->pwm_pulse_width);
        goto end;
    }

    if (debug_flag)
    LOG_I("pwm dev <%s> change channel from <%d> to <%d> ok", pwm_dev_name.c_str(), pwm_channel, new_channel);

    pwm_channel = new_channel;
    return chip_pwm_status_ok;
    end:

    rt_pwm_enable(this->pwm_dev, pwm_channel);
    return chip_pwm_status_fail;
}

chip_pwm_status CHIP_PWM::CHIP_PWM_change_frequency(uint32_t new_freq)
{
    if(init_flag != chip_pwm_status_ok)
    {
        LOG_E("error: pwm dev <%s> channel <%d> init fail,pls check",
              pwm_dev_name.c_str(),
              pwm_channel);
        return chip_pwm_status_fail;
    }

    if (_calculate_pwm_period(new_freq) != chip_pwm_status_ok)
    {
        LOG_E("error: pwm dev <%s> channel <%d> pwm_frequency <%u> fail",
              pwm_dev_name.c_str(),
              pwm_channel,
              new_freq);
        goto end;
    }

    _calculate_pwm_pulse_width(pwm_ratio);

    if (rt_pwm_set(this->pwm_dev, pwm_channel, (uint32_t)this->pwm_period, this->pwm_pulse_width) != RT_EOK)
    {
        LOG_E("error: pwm dev <%s> channel <%d> set period <%u> pluse width <%u> fail",
              pwm_dev_name.c_str(),
              pwm_channel,
              this->pwm_period,
              this->pwm_pulse_width);
        goto end;
    }

    return chip_pwm_status_ok;
    end: return chip_pwm_status_fail;

}

chip_pwm_status CHIP_PWM::CHIP_PWM_change_duty_ratio(double new_ratio) //new_ratio:百分比 0-100
{
    if(init_flag != chip_pwm_status_ok)
    {
        LOG_E("error: pwm dev <%s> channel <%d> init fail,pls check",
              pwm_dev_name.c_str(),
              pwm_channel);
        return chip_pwm_status_fail;
    }

    _calculate_pwm_pulse_width(new_ratio);

    if (rt_pwm_set(this->pwm_dev, pwm_channel, (uint32_t)this->pwm_period, this->pwm_pulse_width) != RT_EOK)
    {
        LOG_E("error: pwm dev <%s> channel <%d> set period <%u> pluse width <%u> fail",
              pwm_dev_name.c_str(),
              pwm_channel,
              this->pwm_period,
              this->pwm_pulse_width);
        goto end;
    }

    rt_mutex_take(pwm_ratio_lock, RT_WAITING_FOREVER);
    this->pwm_ratio = new_ratio;
    rt_mutex_release(pwm_ratio_lock);

    return chip_pwm_status_ok;
    end: return chip_pwm_status_fail;
}

uint8_t CHIP_PWM::CHIP_PWM_read_latest_ratio()
{
    uint8_t ratio;
    rt_mutex_take(pwm_ratio_lock, RT_WAITING_FOREVER);
    ratio = this->pwm_ratio ;
    rt_mutex_release(pwm_ratio_lock);
    return ratio;
}

chip_pwm_status CHIP_PWM::CHIP_PWM_read_init_status()
{
    return this->init_flag;
}
